/*
What about white-listed IP's? Maybe we don't want them
to fill up the lot. The reset will zero these out.

make wrappers for a lot of these stuff to make it
cleaner.
*/
#include <cstdio>
#include <cstdlib>

#include <iostream>
using std::cout;
using std::endl;

#include <string>
using std::string;

#include <map>


typedef std::map< std::string, long, std::less< string > > H;
typedef std::map< std::string, long, std::less< string > > *Hp;

Hp white;




class StringTokenizer {

public:

    StringTokenizer(const string& s, const char* delim = NULL) :
            str_(s), count_(-1), begin_(0), end_(0) {

        if (!delim)
            delim_ = " \f\n\r\t\v";  //default to whitespace
        else
            delim_ = delim;

        // Point to the first token
        begin_ = str_.find_first_not_of(delim_);
        end_ = str_.find_first_of(delim_, begin_);
    }
    ~StringTokenizer() {
        fprintf(stderr,"exit StringTokenizer\n");
    }



    size_t countTokens( ) {
        if (count_ >= 0) // return if we've already counted
            return(count_);

        string::size_type n = 0;
        string::size_type i = 0;

        while (1) {
            // advance to the first token
            if ((i = str_.find_first_not_of(delim_, i)) == string::npos)
                break;
            // advance to the next delimiter
            i = str_.find_first_of(delim_, i+1);
            n++;
            if (i == string::npos)
                break;
        }
        return (count_ = n);
    }
    bool hasMoreTokens( ) {
        return(begin_ != end_);
    }
    void nextToken(string& s) {
        if (begin_ != string::npos && end_ != string::npos) {
            s = str_.substr(begin_, end_-begin_);
            begin_ = str_.find_first_not_of(delim_, end_);
            end_ = str_.find_first_of(delim_, begin_);
        }
        else if (begin_ != string::npos && end_ == string::npos)
        {
            s = str_.substr(begin_, str_.length( )-begin_);
            begin_ = str_.find_first_not_of(delim_, end_);
        }

    }

private:
    StringTokenizer( ) {};
    string delim_;
    string str_;
    int count_;
    int begin_;
    int end_;
};










void hpadd(Hp hp,string *ipu) {

    H::const_iterator result = hp->find(*ipu);
    if (result != hp->end()){
        (*hp)[*ipu]+=1;
    } else {
        (*hp)[*ipu]=1;
    }

    //hp->insert(H::value_type(*ipu,1));
}



long hpadd(Hp hp,const char *s) {
    string ipu=s;

    H::const_iterator result = hp->find(ipu);
    if (result != hp->end()){
        (*hp)[ipu]+=1;
    } else {
        (*hp)[ipu]=1;
    }
    return (*hp)[ipu];
    //hp->insert(H::value_type(*ipu,1));
}


long _validate(Hp hp, string ipu){
    string t=ipu.substr(ipu.find_first_of(":")+1);

    H::const_iterator wresult = white->find(t);
    if (wresult != white->end()) {
        if ((*hp)[ipu] >= 5) {
            return -1;
        }
    }

    H::const_iterator result = hp->find(ipu);
    if (result != hp->end()){
        (*hp)[ipu]+=1;
    } else {
        (*hp)[ipu]=1;
    }
    return (*hp)[ipu];
    //hp->insert(H::value_type(*ipu,1));
}


long validate(Hp hp,string s) {
    string ipu(s);
    return _validate(hp, ipu);
}

long validate(Hp hp,const char *s) {
    string ipu(s);
    return _validate(hp, ipu);
}

long validate(Hp hp,const char *ip,const char *acct) {
    char s[100];
    snprintf(s,100,"%s:%s",ip,acct);
    string ipu(s);
    return _validate(hp, ipu);
}

long _reset(Hp hp, string ipu){
    string t=ipu.substr(ipu.find_first_of(":")+1);

    H::const_iterator wresult = white->find(t);
    if (wresult != white->end()) {
        if ((*hp)[ipu] >= 5) {
            (*hp)[ipu]=-5; // give extra chances?
        } else {
            (*hp)[ipu]=0; // give extra chances?
        }
    }

    H::const_iterator result = hp->find(ipu);
    if (result != hp->end()){
        (*hp)[ipu]+=1;
    } else {
        (*hp)[ipu]=1;
    }
    return (*hp)[ipu];
    //hp->insert(H::value_type(*ipu,1));
}




long reset(Hp hp,const char *ip,const char *acct) {
    char s[100];
    snprintf(s,100,"%s:%s",ip,acct);
    string ipu(s);
    return _reset(hp,ipu);
}


long reset(Hp hp,string s) {
    string ipu=s;
    return _reset(hp,ipu);
}



long _whiteadd(Hp hp,string ipu) {

    H::const_iterator result = hp->find(ipu);
    if (result != hp->end()){
        (*hp)[ipu]+=1;
    } else {
        (*hp)[ipu]=1;
    }
    return (*hp)[ipu];
    //hp->insert(H::value_type(*ipu,1));
}

long whiteadd(Hp hp,const char *s) {
    string ipu(s);
    return _whiteadd(hp,ipu);
}

long whiteadd(Hp hp,string s) {
    string ipu(s);
    return _whiteadd(hp,ipu);
}


int main()
{

    Hp hp = new H();
    white = new H();
    string swhiteinput="chirico mchirico dev.chirico";
    string tmp;

    StringTokenizer st(swhiteinput);
    while (st.hasMoreTokens( )) {
        st.nextToken(tmp);
        whiteadd(white,tmp);
    }



    string ipu="192.168.2.34:chirico";

    for (int i=0; i < 20; ++i) {
        // This breaks out after 6 at the particular IP address
        // for an account on the whiltelist. Only problem is that
        // someone could try from different IP addresses to the
        // same account, say over a short time period.
        if ( validate(hp,"192.168.2.34","chirico") < 0 )
            break;
    }


    for (int i=0; i < 30; ++i)
        validate(hp,"192.168.2.30:dev.chirico");

    for (int i=0; i < 30; ++i)
        validate(hp,"192.168.2.30:bozo");

    string s = "192.123.4.2:chirico";
    fprintf(stderr,"%s\n",s.substr(s.find_first_of(":")+1).c_str());


    for (H::const_iterator iter = hp->begin();
            iter != hp->end(); ++iter )
        cout << iter->first << '\t' << iter->second << '\n';

    cout << "\n\n\n\n";
    reset(hp,"192.168.2.34","chirico");


    for (H::const_iterator iter = hp->begin();
            iter != hp->end(); ++iter )
        cout << iter->first << '\t' << iter->second << '\n';

    delete(white);
    delete(hp);
    return 0;
}
